package com.lineage.server.serverpackets;

import com.lineage.config.Config;
import com.lineage.config.ConfigBad;
import com.lineage.server.utils.RandomArrayList;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public abstract class ServerBasePacket extends OpcodesServer {
  private static final Log _log = LogFactory.getLog(ServerBasePacket.class);
  
  private static final String CLIENT_LANGUAGE_CODE = Config.CLIENT_LANGUAGE_CODE;
  
  protected ByteArrayOutputStream _bao = new ByteArrayOutputStream();
  
  private static final int TAG_TYPE_BITS = 3;
  
  private static final int WIRETYPE_LENGTH_DELIMITED = 2;
  
	private static final int WIRETYPE_VARINT = 0;
	
	
	/** 1 x 1 */
	protected void randomByte() {
		this.writeC((byte) RandomArrayList.getInt(256));
	}

	/** 1 x 2 */
	protected void randomShort() {
		this.randomByte();
		this.randomByte();
	}

	/** 1 x 3 */
	protected void randomShort3() {
		this.randomByte();
		this.randomByte();
		this.randomByte();
	}

	/** 2 x 2 */
	protected void randomInt() {
		this.randomShort();
		this.randomShort();
	}

	/** 2 x 3 */
	protected void randomInt6() {
		this.randomShort();
		this.randomInt();
	}

	/** 4 x 4 */
	protected void randomLong() {
		this.randomInt();
		this.randomInt();
	}
  
  protected Object writeBoolean(boolean b) {
    this._bao.write(b ? 1 : 0);
    return null;
  }
  
  protected void writeD(int value) {
    this._bao.write(value & 0xFF);
    this._bao.write(value >> 8 & 0xFF);
    this._bao.write(value >> 16 & 0xFF);
    this._bao.write(value >> 24 & 0xFF);
  }
  
  protected void writeH(int value) {
    this._bao.write(value & 0xFF);
    this._bao.write(value >> 8 & 0xFF);
  }
  
  protected void writeC(int value) {
    this._bao.write(value & 0xFF);
  }
  
  protected void writeP(int value) {
    this._bao.write(value);
  }
  
  protected void writeL(long value) {
    this._bao.write((int)(value & 0xFFL));
  }
  
  protected void writeEXP(long value) {
    this._bao.write((int)(value & 0xFFL));
    this._bao.write((int)(value >> 8L & 0xFFL));
    this._bao.write((int)(value >> 16L & 0xFFL));
    this._bao.write((int)(value >> 24L & 0xFFL));
  }
  
  protected void writeF(double org) {
    long value = Double.doubleToRawLongBits(org);
    this._bao.write((int)(value & 0xFFL));
    this._bao.write((int)(value >> 8L & 0xFFL));
    this._bao.write((int)(value >> 16L & 0xFFL));
    this._bao.write((int)(value >> 24L & 0xFFL));
    this._bao.write((int)(value >> 32L & 0xFFL));
    this._bao.write((int)(value >> 40L & 0xFFL));
    this._bao.write((int)(value >> 48L & 0xFFL));
    this._bao.write((int)(value >> 56L & 0xFFL));
  }
  
  protected void writeExp(long value) {
    this._bao.write((int)(value & 0xFFL));
    this._bao.write((int)(value >> 8L & 0xFFL));
    this._bao.write((int)(value >> 16L & 0xFFL));
    this._bao.write((int)(value >> 24L & 0xFFL));
  }
  
  protected void writeS(String text) {
    try {
      if (text != null) {
        String chtext = text;
        Iterator<String> iter = ConfigBad.BAD_TEXT_LIST.iterator();
        while (iter.hasNext()) {
          String bad = iter.next();
          int index = chtext.indexOf(bad);
          if (index != -1) {
            chtext = text.substring(0, index);
            chtext = String.valueOf(String.valueOf(chtext)) + text.substring(index + bad.length());
          } 
        } 
        this._bao.write(chtext.getBytes(CLIENT_LANGUAGE_CODE));
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
    this._bao.write(0);
  }
  
	protected void writeByteArray(final int fieldNumber, final byte[] value) {
		this.writeRawVarint32(fieldNumber << TAG_TYPE_BITS
				| WIRETYPE_LENGTH_DELIMITED);
		try {
			if (value != null) {
				this.writeRawVarint32(value.length);
				_bao.write(value);
			} else {
				this.writeRawVarint32(0);
			}
		} catch (final Exception e) {
			_log.error(e.getLocalizedMessage(), e);
		}
	}
  
  protected void writeByte(byte[] text) {
    try {
      if (text != null)
        this._bao.write(text); 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
    this._bao.write(0);
  }
  
	private void writeRawVarint32(int value) {
		while (true) {
			if ((value & ~0x7F) == 0) {
				this._bao.write(value);
				return;
			} else {
				this._bao.write(value & 0x7F | 0x80);
				value >>>= 7;
			}
		}
	}
	
	private void writeRawVarint64(long value) {
		while (true) {
			if ((value & ~0x7FL) == 0) {
				this._bao.write((int) value);
				return;
			} else {
				this._bao.write((int) value & 0x7F | 0x80);
				value >>>= 7;
			}
		}
	}
  
	protected void writeInt32(final int fieldNumber, final int value) {
		this.writeRawVarint32(fieldNumber << TAG_TYPE_BITS | WIRETYPE_VARINT);
		if (value >= 0) {
			this.writeRawVarint32(value);
		} else {
			this.writeRawVarint64(value);
		}
	}
  
  protected byte[] getBytes() {
    int padding = this._bao.size() % 8;
    if (padding != 0) {
      int i = padding;
      while (i < 8) {
        writeC(0);
        i++;
      } 
    } 
    return this._bao.toByteArray();
  }
  
  public abstract byte[] getContent() throws IOException;
  
  public String getType() {
    return getClass().getSimpleName();
  }
}
